NMDC - Direct Connect protocol
This page contains some code used to generate a $Key from a $Lock in a variety of languages.
[edit] Example One
#include <string.h>
#include <stdlib.h>
/* erealloc done by James N. Hart
Abridged version.
INPUT:
void **mem
- should be a pointer to the pointer that you want to allocate or
reallocate memory to. If *mem == NULL (0) then data will be allocated
from scratch. If != NULL then data will be realloc'ed.
int size
- should be the size of the memory you want allocated. *note this
revision doesn't do any checking for size 0. Results are undefined if
size == 0..
OUTPUT:
void **mem
- assigned with allocated memory. Memory is initialized with all
bits 0.
PURPOSE:
To give one interface for malloc and realloc, to initialized malloc'ed
data, and to do error checking and handling.
ABRIDGED NOTE:
There are comments where you should add your own error handling code.
EXAMPLE:
Create a character array of 35 characters, then resize it to 25.
char *string = 0; erealloc((void **)&string, sizeof(char)*35);
erealloc((void **)&string, sizeof(char)*25);
*/
void erealloc(void **mem, size_t size)
{
int err = 0;
if(!*mem)
{
if(!(*mem = malloc(size)))
{
/*You should handle a memory allocation error here */
exit(1);
}
memset(*mem, 0, size);
return;
}
if(! (*mem = realloc(*mem, size) ) )
{
/*You should handle a memory allocation error here */
exit(1);
}
}
/* generateKey done by James N. Hart
INPUT:
unsigned char *lock
- A null terminated character array that contains the lock posted with
$Lock (excluding $Lock= and ' PK=*')
unsigned char **fkey
- a pointer to a previous allocated char *array or a pointer to a
char *array that has been initialized wiht NULL (0).
OUTPUT:
unsigned char **fkey
- assigned a allocated char *array, null terminated that contains
the key.
PURPOSE:
To generate the key from given lock for as specified for the
DC Protocol.
BUGS:
If you want to use this in a real program, you may want to add error
handling to check that the lock is at least 2 characters long. (if not
this code should crash)
EXAMPLE:
char *lock = "IEatHamsters", *key = 0;
generateKey(lock, &key);
*/
void generateKey(unsigned char *lock, unsigned char **fkey)
{
int count = 0, len = 0, offset = 0;
char *key = 0, *tkey= 0;
/* Get the length of the lock */
len = strlen(lock);
/* Initialize the key memory */
erealloc((void **)&key, sizeof(char)*(len + 1) );
/* assign key[1 .. len - 1] accoarding to lock to key specs */
while(lock[++count])
key[count] = lock[count] ^ lock[count - 1];
/* assign key[0] with the special data */
key[0] = lock[0] ^ lock[len - 1] ^ lock[len - 2] ^ 5;
count = 0;
/* Swap 4 bits of each result in Key at this point */
while(key[count++])
key[count - 1] = ((key[count - 1] << 4)) | ((key[count - 1] >> 4));
count = 0;
/* tkey stands for Temp Key, it is needed to write out the encoded key */
erealloc((void **)fkey, sizeof(char)*(len + 1));
tkey = *fkey;
while(key[count++])
{
/* If a byte has any of the following values in the case statement it needs
to be encoded in the format /%DCNxxx%/ where xxx is the ascii value of
the character */
switch(key[count - 1])
{
case 0:
case 5:
case 36:
case 96:
case 124:
case 126:
erealloc((void **)&tkey, sizeof(char)*(len + offset + 11));
sprintf(&tkey[count - 1 + offset], "/%%DCN%.3d%%/", key[count - 1]);
/* offset let's us keep track of where we are in tkey in comparison to
key. Every time we encode a character we have a diffrence of 9 more
characters offseted in tkey in relation to key
*/
offset += 9;
break;
/* We don't need to encode this character */
default:
tkey[count - 1 + offset] = key[count - 1];
break;
}
}
/* Give our string its ending null */
tkey[len + offset] = 0;
/* Assign memory to fkey */
*fkey = tkey;
/* Free key */
free(key);
}
[edit] Example Two
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//From rapsys : http://rapsys.free.fr/ (Under GPL v2 or more license)
/**
* Compute the access key from the $Lock string
* @param lock the lock string
* @return the computed key
*/
char *dc_compute_access_key(char *lock)
{
int count, len, offset;
char *key, *tkey;
unsigned char *fkey;
//Default value
offset = 0;
key = NULL;
tkey = NULL;
// Get the length of the lock
len = strlen(lock);
//Initialize the key memory
key = realloc(key, sizeof(char)*(len + 1) );
if (key == NULL)
{
fprintf(stderr,"Error while realloc key");
return NULL;
}
//Assign key[1 .. len - 1] accoarding to lock to key specs
count = 1;
while(lock[count])
{
key[count] = lock[count] ^ lock[count - 1];
count++;
}
//Assign key[0] with the special data
key[0] = lock[0] ^ lock[len - 1] ^ lock[len - 2] ^ 5;
count = 0;
//Swap 4 bits of each result in Key at this point
while(key[count])
{
count++;
key[ ((key[count - 1|count - 1] = ((key[count - 1] << 4)) |] >> 4));
}
count = 0;
/* tkey stands for Temp Key, it is needed to write out the encoded key */
tkey = realloc(tkey, sizeof(char)*(len + 1));
if (tkey == NULL)
{
fprintf(stderr,"Error while realloc tkey");
return NULL;
}
fkey = (unsigned char*)tkey;
while(key[count++])
{
/* If a byte has any of the following values in the case statement it needs
* to be encoded in the format /%DCNxxx%/ where xxx is the ascii value of
* the character
*/
switch(key[count - 1])
{
case 0:
case 5:
case 36:
case 96:
case 124:
case 126:
tkey = realloc(tkey, sizeof(char)*(len + offset + 11));
if (tkey == NULL)
{
fprintf(stderr,"Error while realloc fkey");
return NULL;
}
sprintf(&tkey[count - 1 + offset], "/%%DCN%.3d%%/", key[count - 1]);
/* offset let's us keep track of where we are in tkey in comparison to
* key. Every time we encode a character we have a diffrence of 9 more
* characters offseted in tkey in relation to key
*/
offset += 9;
break;
default:
// We don't need to encode this character
tkey[count - 1 + offset] = key[count - 1];
}
}
//Give our string its ending null
tkey[len + offset] = 0;
//Free key
free(key);
return tkey;
}
int main(int argc, char** argv)
{
printf("%s",dc_compute_access_key("somelockstring"));
return 0;
}
[edit] Example Three
#include <stdlib.h>
/* Taken from Dolda Connect -- Licensed under the GPL */
static int reservedchar(unsigned char c)
{
return((c == 0) || (c == 5) || (c == 124) || (c == 96) || (c == 126) || (c == 36));
}
static char *dcmakekey(char *lock)
{
int i, len, offset;
char *buf, *key;
char save;
/* Step 1: Compute key */
buf = malloc(strlen(lock));
save = 5;
len = 0;
for(i = 0; lock[i]; i++) {
buf[i] = lock[i] ^ save;
buf[i] = ((buf[i] & 0x0F) << 4) | ((buf[i] & 0xF0) >> 4);
save = lock[i];
if((i != 0) && reservedchar(buf[i]))
len += 10;
else
len++;
}
buf[0] ^= buf[i - 1];
if(reservedchar(buf[0]))
len += 10;
else
len++;
/* Step 2: Quote reserved characters */
key = malloc(len + 1);
offset = 0;
for(i = 0; lock[i] != 0; i++) {
if(reservedchar(buf[i]))
offset += sprintf(key + offset, "/%%DCN%03i%%/", buf[i]);
else
key[offset++] = buf[i];
}
key[offset] = 0;
free(buf);
/* Observe: The caller will have to free the memory */
return(key);
}
sub lock2key
{
my @lock = split( // , shift );
my $i;
my @key = ();
# convert to ordinal
foreach( @lock ) {
$_ = ord;
}
# calc key[0] with some xor-ing magic
push( @key , $lock[0] ^ 5 );
# calc rest of key with some other xor-ing magic
for( $i = 1 ; $i < @lock ; $i++ ) {
push( @key , ( $lock[$i] ^ $lock[$i - 1] ) );
}
# nibble swapping
for( $i = 0 ; $i < @key ; $i++ ) {
$key[$i] = ( (($key[$i] << 4) & 240) | ( ($key[$i] >> 4) & 15 )) & 0xff;
}
#temp[0] = (u_int8_t)(temp[0] ^ temp[aLock.length()-1]);
$key[0] = $key[0] ^ $key[ @key - 1 ];
# escape some
foreach( @key ) {
if ( $_ == 0 || $_ == 5 || $_ == 36 || $_ == 96 || $_ == 124 || $_ == 126 ) {
$_ = sprintf( '/%%DCN%03i%%/' , $_ );
} else {
$_ = chr;
}
}
# done
return join( "" , @key );
}
[edit] Example One (Not work!)
//--------------------------------
// Done by [RO]VeNoM
// razvan_stanga@yahoo.com
//--------------------------------
function lock2key($_LOCK) {
$lockLength = strlen ($_LOCK);
$h = ord($_LOCK[0]) ^ ord( $_LOCK[ $lockLength - 1] ) ^ ord( $_LOCK[ $lockLength - 2] ) ^ 5;
echo $h . "<br>";
while ($h > 255) {$h = $h - 256;}
$h = (($h<<4) & 240) || (($h>>4) & 15);
$a = $h;
if ($a == '126' or // '~'
$a == '124' or // '||'
$a == '96' or // '`'
$a == '36' or // '$'
$a == '5' or // '^E'
$a == '0') // NUL
{
$LockToKey = "/%DCN";
if ($a < 100)
$LockToKey .="0";
if ($a < 10)
$LockToKey .="0";
$LockToKey .= $a; // As a string integer
$LockToKey .= "%/";
} else {
$LockToKey = chr ($a); // No transformation.
}
for ($j = 1; $j < strlen($_LOCK); $j++) {
$h = ord($_LOCK[$j]) ^ ord($_LOCK[$j-1]);
while ($h > 255) {$h = $h - 256;}
$h = (($h<<4) & 240) || (($h>>4) & 15);
echo $j . " - " . ($h*16) . "<br>";
$a = $h;
if ($a == '126' or // '~'
$a == '124' or // '||'
$a == '96' or // '`'
$a == '36' or // '$'
$a == '5' or // '^E'
$a == '0') // NUL
{
$LockToKey .= "/%DCN";
if ($a < 100)
$LockToKey .="0";
if ($a < 10)
$LockToKey .="0";
$LockToKey .= $a; // As a string integer
$LockToKey .= "%/";
} else {
$LockToKey .= chr ($a); // No transformation.
}
}
return $LockToKey;
}
[edit] Example Two
//----------
// owiec at barbara . eu . org [PL]
//----------
function keygen($lock)
{
$len = strlen($lock);
$key = array();
$key[0] = ord($lock[0]) ^ ord($lock[$len-1]) ^ ord($lock[$len-2]) ^ 5;
for ($i = 1; $i < $len; $i++)
$key[$i] = ord($lock[$i]) ^ ord($lock[$i-1]);
for ($i = 0; $i < $len; $i++)
$key[$i] = (($key[$i]<<4) & 240) | (($key[$i]>>4) & 15);
$key = array_map('chr',$key);
for($i = 0; $i<$len; $i++)
{
if( $key[$i] == chr(0))
$key[$i] = '/%DCN000%/';
if( $key[$i] == chr(5))
$key[$i] = '/%DCN005%/';
if( $key[$i] == chr(36))
$key[$i] = '/%DCN036%/';
if( $key[$i] == chr(96))
$key[$i] = '/%DCN096%/';
if( $key[$i] == chr(124))
$key[$i] = '/%DCN124%/';
if( $key[$i] == chr(126))
$key[$i] = '/%DCN126%/';
}
$key = implode('',$key);
return $key;
}
[edit] Example Three
/**********************************
* lock2key *
* ------------------- *
* Sunday, January 27, 2007 *
* (C) 2007 The Freeman Group *
* emdfreeman [at] mail . ru *
* *
**********************************/
function lock2key($lock){
$len = strlen($lock);
$key[0] = ord($lock[0]) ^ ord($lock[$len-1]) ^ ord($lock[$len-2]) ^ 5;
for ($i = 1; $i < $len; $i++) $key[$i] = ord($lock[$i]) ^ ord($lock[$i-1]);
for ($i = 0; $i < $len; $i++) $key[$i] = (($key[$i]<<4) & 240) | (($key[$i]>>4) & 15);
$key = array_map('chr',$key);
for($i = 0; $i<$len; $i++){
switch($key[$i]){
case chr(0):
$key[$i] = '/%DCN000%/';
break;
case chr(2):
$key[$i] = '/%DCN005%/';
break;
case chr(36):
$key[$i] = '/%DCN036%/';
break;
case chr(96):
$key[$i] = '/%DCN096%/';
break;
case chr(124):
$key[$i] = '/%DCN124%/';
break;
case chr(126):
$key[$i] = '/%DCN126%/';
break;
}
}
$key = implode("",$key);
return $key;
}
/**********************************
* lock2key *
* ------------------- *
* Sunday, January 27, 2007 *
* (C) 2007 The Freeman Group *
* emdfreeman [at] mail . ru *
* *
**********************************/
function lock2key($lock){
$len = strlen($lock);
$key[0] = ord($lock[0]) ^ ord($lock[$len-1]) ^ ord($lock[$len-2]) ^ 5;
for ($i = 1; $i < $len; $i++) $key[$i] = ord($lock[$i]) ^ ord($lock[$i-1]);
for ($i = 0; $i < $len; $i++) $key[$i] = (($key[$i]<<4) & 240) | (($key[$i]>>4) & 15);
$key = array_map('chr',$key);
for($i = 0; $i<$len; $i++){
for($z = 0; $z<240; $z++){
if(strlen($z) === 1 ) if( $key[$i] == chr($z)) $key[$i] = '/%DCN00'.$z.'%/';
}
}
$key = implode("",$key);
return $key;
}
[edit] Visual BASIC
[edit] Example One
'--------------------------------
' Done by Ofca
' ofca@ofca.pl
'--------------------------------
' Wiki edit by bluebear: Old style VB6 LockToKey; would probably not work with the newer versions of hub software
Function Lock2Key(StrLock As String) As String
Dim TLock2Key As String, TChar As Integer
If Len(StrLock) < 3 Then
Lock2Key = Left$("BROKENCLIENT", Len(StrLock))
Exit Function
End If
TLock2Key = Chr$(Asc(Left$(StrLock, 1)) Xor Asc(Right$(StrLock, 1)) Xor Asc(Mid$(StrLock, Len(StrLock) - 1, 1)) Xor 5)
For i = 2 To Len(StrLock)
TLock2Key = TLock2Key & Chr$(Asc(Mid$(StrLock, i, 1)) Xor Asc(Mid$(StrLock, i - 1, 1)))
Next i
For i = 1 To Len(TLock2Key)
TChar = Asc(Mid$(TLock2Key, i, 1))
TChar = TChar * 16 + TChar 16 'Swap bits 11110000 -> 00001111
TChar = TChar Mod 256
If TChar = 0 Or TChar = 5 Or TChar = 36 Or TChar = 96 Or TChar = 124 Or TChar = 126 Then
Lock2Key = Lock2Key & "/%DCN" & Right$("000" & TChar,3) & "%/"
Else
Lock2Key = Lock2Key & Chr$(TChar)
End If
Next i
End Function
[edit] Example Two
'------------------------------------------------
'Function made by Massimiliano (Neo) from Italy -
'------------------------------------------------
' Wiki edit by bluebear: This LockToKey was acutally posted as a vb.net converter wich it's not.
' I've havent tryed this one, but guess it will work for vb6.
Public Function LockToKey(Lck As String) As String
' Edited by •--Gàu®áv™--•, the old LockToKey wasn't working properly as it was made for vb.net converter
Dim h As Integer, j As Integer
n = 5
h = InStr(1, Lck, " ")
If h Then Lck = Left$(Lck, h - 1)
h = Asc(Lck) Xor Asc(Right$(Lck, 1)) Xor Asc(Right$(Lck, 2)) Xor n
h = (h \ 16) Xor (h * 16)
Do While h > 255
h = h - 256
Loop
Select Case h
Case 0, 5, 36, 96, 124, 126
LockToKey = "/%DCN" & Right$("00" & CStr(h), 3) & "%/"
Case Else
LockToKey = Chr$(h)
End Select
For j = 2 To Len(Lck)
h = Asc(Mid$(Lck, j, 1)) Xor Asc(Mid$(Lck, j - 1, 1))
h = (h \ 16) Xor (h * 16)
Do While h > 255
h = h - 256
Loop
Select Case h
Case 0, 5, 36, 96, 124, 126
LockToKey = LockToKey & "/%DCN" & Right$("00" & CStr(h), 3) & "%/"
Case Else
LockToKey = LockToKey & Chr$(h)
End Select
Next
End Function
[edit] Visual BASIC .NET
Public Function LocktoKey(ByVal Lck As String) As String
'// This LockToKey is for VB.NET
'// Written by bluebear - http://www.thewildplace.dk/
'// It uses the .net framework string class instead of the old vb6 style
'// If this don't work for you it's because you use the wrong encoding on inbound/outbound data
'// Expects lock with "$Lock" and PK in string.
'//
Lck = Lck.Replace("$Lock ", "")
Dim sKey As String
Dim sTmp As String
Dim iLen As Byte
Dim iChar As Integer
Dim iPos As Integer = Lck.IndexOf(" ", 1)
If CBool(iPos) Then Lck = Lck.Substring(0, iPos)
iChar = Asc(Lck) Xor Asc(Lck.Substring(Lck.Length - 1)) Xor Asc(Lck.Substring(Lck.Length - 2, 1)) Xor 5
iChar = (iChar \ 16) Xor (iChar * 16)
Do While iChar > 255
iChar = iChar - 256
Loop
Select Case iChar
Case 0, 5, 36, 96, 124, 126
sTmp = "00" & CStr(iChar)
iLen = CByte(sTmp.Length)
If iLen > 3 Then iLen -= CByte(3) Else iLen = 0
sKey = "/%DCN" & sTmp.Substring(iLen) & "%/"
Case Else
sKey = Chr(iChar)
End Select
For iPos = 1 To Lck.Length - 1
iChar = Asc(Lck.Substring(iPos, 1)) Xor Asc(Lck.Substring(iPos - 1, 1))
iChar = (iChar \ 16) Xor (iChar * 16)
Do While iChar > 255
iChar = iChar - 256
Loop
Select Case iChar
Case 0, 5, 36, 96, 124, 126
sTmp = "00" & CStr(iChar)
iLen = CByte(sTmp.Length)
If iLen > 3 Then iLen -= CByte(3) Else iLen = 0
sKey += "/%DCN" & sTmp.Substring(iLen) & "%/"
Case Else
sKey += Chr(iChar)
End Select
Next
Return sKey
End Function
[edit] REALbasic 5.5
// Add a function: Protected Function Lock2Key(sLock As String) As String
// REALBasic Lock To Key converter
// Written in REALBasic 5.5 Pro by Bluebear
// http://www.thewildplace.dk/
// Rember to use the correct encoding for incoming/outgoing data
Dim Lck As String, key As String
Dim iChar As Integer, iPos As Integer
Lck = Replace(sLock, "$Lock ", "")
iPos = InStr(1, Lck, " ")
If iPos > 0 Then Lck = Left(Lck, iPos - 1)
// 2005 //iChar = Bitwise.BitXor(Asc(Lck), Asc(Right(Lck, 1)), Asc(Right(Lck, 2)), 5)
iChar = Bitwise.BitXor(Bitwise.BitXor(Asc(Lck), Asc(Right(Lck, 1))),Bitwise.BitXor(Asc(Right(Lck, 2)), 5))
iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
While iChar > 255
iChar = iChar - 256
Wend
Select Case iChar
Case 0, 5, 36, 96, 124, 126
key = "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
Case Else
key = Chr(iChar)
End Select
For iPos=2 To Len(Lck)
iChar = Bitwise.BitXor(Asc(Mid(Lck, iPos, 1)), Asc(Mid(Lck, iPos - 1, 1)))
iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
While iChar > 255
iChar = iChar - 256
Wend
Select Case iChar
Case 0, 5, 36, 96, 124, 126
key = key + "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
Case Else
key = key + Chr(iChar)
End Select
Next
Return key
[edit] REALbasic 2005
// Add as function: Function Lock2Key(sLock As String) As String
// REALBasic Lock To Key converter
// Written in REALBasic 2005 Pro by Bluebear
// http://www.thewildplace.dk/
// Rember to use the correct encoding for incoming/outgoing data
Dim Lck As String, key As String
Dim iChar As Integer, iPos As Integer
Lck = Replace(sLock, "$Lock ", "")
iPos = InStr(1, Lck, " ")
If iPos > 0 Then Lck = Left(Lck, iPos - 1)
iChar = Bitwise.BitXor(Asc(Lck), Asc(Right(Lck, 1)), Asc(Right(Lck, 2)), 5)
iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
While iChar > 255
iChar = iChar - 256
Wend
Select Case iChar
Case 0, 5, 36, 96, 124, 126
key = "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
Case Else
key = Chr(iChar)
End Select
For iPos=2 To Len(Lck)
iChar = Bitwise.BitXor(Asc(Mid(Lck, iPos, 1)), Asc(Mid(Lck, iPos - 1, 1)))
iChar = Bitwise.BitXor((iChar / 16), (iChar * 16))
While iChar > 255
iChar = iChar - 256
Wend
Select Case iChar
Case 0, 5, 36, 96, 124, 126
key = key + "/%DCN" + Right("00" + CStr(iChar), 3) + "%/"
Case Else
key = key + Chr(iChar)
End Select
Next
Return key
[edit] Python
[edit] Example One
# by Benjamin Bruheim
def lock2key(lock):
"Generates response to $Lock challenge from Direct Connect Servers"
lock = [ord(c) for c in lock]
key = [0]
for n in range(1,len(lock)):
key.append(lock[n]^lock[n-1])
key[0] = lock[0] ^ lock[-1] ^ lock[-2] ^ 5
for n in range(len(lock)):
key[n] = ((key[n] << 4) | (key[n] >> 4)) & 255
result = ""
for c in key:
if c in [0, 5, 36, 96, 124, 126]:
result += "/%%DCN%.3i%%/" % c
else:
result += chr(c)
return result
if __name__=="__main__":
key = lock2key("T&AUreb/M_2Wtp_lZU)EA_yU_)2[2/_4u:,`L`3\\m:+ctsnyw9@")
assert key=="\x82'vArqp\xd4&!\xd6V2@\xf23c\xf0\xc7\xc6@\xe1b\xc2\xa0g" \
+ "\xb1\x96\x96\xd1\x07\xb6\x14\xf4a\xc4\xc2\xc25\xf6\x13u\x11" \
+ "\x84qp\xd1q\xe0\xe4\x97"
[edit] Example Two
# by Benjamin Bruheim, optimized by Dody Suria Wijaya (25% faster)
import array
def lock2key2(lock):
"Generates response to $Lock challenge from Direct Connect Servers"
lock = array.array('B', lock)
ll = len(lock)
key = list('0'*ll)
for n in xrange(1,ll):
key[n] = lock[n]^lock[n-1]
key[0] = lock[0] ^ lock[-1] ^ lock[-2] ^ 5
for n in xrange(ll):
key[n] = ((key[n] << 4) | (key[n] >> 4)) & 255
result = ""
for c in key:
if c in (0, 5, 36, 96, 124, 126):
result += "/%%DCN%.3i%%/" % c
else:
result += chr(c)
return result
# by Cez
proc lock2key { lock } {
set key ""
set temp ""
set length [string length $lock]
append temp [asc [expr [chr [string index $lock 0]] ^ [chr [string index $lock [expr $length-1]]] ^ [chr [string index $lock [expr $length-2]]] ^ 5]]
for { set x 1 } { $x<$length } { incr x } {
append temp [asc [expr [chr [string index $lock $x]] ^ [chr [string index $lock [expr $x-1]]]]]
}
for { set x 0 } { $x<$length } { incr x } {
append key [asc [expr ([chr [string index $temp $x]] << 4) & 240 | ([chr [string index $temp $x]] >> 4) & 15]]
}
regsub -all -- {\0} $key "/%DCN000%/" key
regsub -all -- {\^E} $key "/%DCN005%/" key
regsub -all -- {\$} $key "/%DCN036%/" key
regsub -all -- {\`} $key "/%DCN096%/" key
regsub -all -- {\|} $key "/%DCN124%/" key
regsub -all -- {\~} $key "/%DCN126%/" key
return $key
}
proc asc i {
if { $i<0 || $i>255 } { error "asc:Integer out of range 0-255" }
return [format %c $i ]
}
proc chr { c } {
set v 0
scan $c %c v
return $v
}
[edit] Delphi
[edit] Example One
// By ParadoxHeart
// Ported from JavaDC's key generator
// This version requires you to cut off the $Lock and pk parts *before* passing the lock code to LockToKey
// I'll post a version that deals with those itself when I get around to it. :p
uses
SysUtils, StrUtils;
function LockToKey(const LockCode: String): String;
var
i, j: Integer;
begin
if Length(LockCode) < 3 then
begin
Result:= 'ERR_INVALIDLOCK';
Exit;
end;
for i:= 1 to Length(LockCode) do
begin
j:= Ord(LockCode[i]);
if i = 1 then
j:= j xor 5
else
j:= j xor Ord(LockCode[i - 1]);
j:= j + ((j mod 17) * 15);
while j > 255 do
Dec(j, 255);
Result:= Result + Chr(j);
end;
Result[1]:= Chr(Ord(Result[1]) xor Ord(Result[Length(Result)]));
AnsiReplaceStr(Result, #0, '/%DCN000%/');
AnsiReplaceStr(Result, #5, '/%DCN005%/');
AnsiReplaceStr(Result, #36, '/%DCN036%/');
AnsiReplaceStr(Result, #96, '/%DCN096%/');
AnsiReplaceStr(Result, #124, '/%DCN124%/');
AnsiReplaceStr(Result, #126, '/%DCN126%/');
end;
[edit] Example Two
//New Delphi Lock2Key ( working perfectly )
public
{ Public declarations }
function Key(iLock: String; magic: byte): String;
function ns(eightbit: byte): byte;
//The above declarations must be placed under the public diclarations
function TF_Dark_Deeds.Key(iLock: String; magic: byte): String;
var tmp,Lock : string;
i : integer;
a,b : byte;
begin
if not (pos(' Pk',ilock) = 0) then
lock := copy(ilock,1,pos(' Pk',ilock)-1)
else lock := ilock;
a := ord(lock[length(lock)-1]);
b := ord(lock[length(lock)]);
tmp := chr(ns(ord(lock[1]) xor a xor b xor magic));
for i := 2 to length(lock) do
begin
tmp := tmp + chr(ns(ord(lock[i]) xor ord(lock[i-1])));
end;
for i := 1 to length(tmp) do
begin
case ord(tmp[i]) of
0 : result := concat(result,'/%DCN000%/');
5 : result := concat(result,'/%DCN005%/');
36 : result := concat(result,'/%DCN036%/');
96 : result := concat(result,'/%DCN096%/');
124 : result := concat(result,'/%DCN124%/');
126 : result := concat(result,'/%DCN126%/');
else result := concat(result,tmp[i]);
end;
end;
end;
function TF_Dark_Deeds.ns(eightbit: byte): byte;
begin
result := (eightbit shl 4) or (eightbit shr 4);
end;
//Use the function like this -> key:=LockToKey(Lock,5); // just put a 5 i don't know why
//by the way tnx to a friend of mine i posted this function
[edit] Delphi.NET
// By jazper jazper@zaboo.net
// Ported ParadoxHearts code(above)
// This function also assumes you have stripped $Lock and remove everything from Pk= onwards
uses
System.Text;
type TBuffArray: Array of byte;
function TDCHub.LockToKey(Data: TBuffArray): &string;
var
i,j,Len: Int32;
Key: TBuffArray;
begin
Len := System.Array(Data).Length;
SetLength(Key,Len);
if Len < 3 then
begin
Result:= 'ERR_INVALIDLOCK';
Exit;
end;
for i:= 0 to Len - 1 do
begin
j:= data[i];
if i = 0 then
j:= j xor 5
else
j:= j xor data[i - 1];
j:= j + ((j mod 17) * 15);
while j > 255 do
Dec(j, 255);
Key[i] := j;
end;
Key[0]:= Key[0] xor Key[Len-1];
//
// Convert to string so we can replace some char's
Result := Encoding.Default.GetString(Key);
// protocol has unaccepted char's, we must replace them
Result := Result.Replace(#0,'/%DCN000%/');
Result := Result.Replace(#5,'/%DCN005%/');
Result := Result.Replace(#36,'/%DCN036%/');
Result := Result.Replace(#96,'/%DCN096%/');
Result := Result.Replace(#124,'/%DCN124%/');
Result := Result.Replace(#126,'/%DCN126%/');
end;
//* Powered By m4st3rB [Poland] m4st3b@o2.pl
// This version requires you to cut off the "$Lock" and "Pk=..." parts before decoding the lock code to Key
//key1
LockLen=Lock.Length();
char L1,Ln1,Ln2=Lock[LockLen-2];
L1=Lock[1];
Ln1=Lock[LockLen-1];
Key1=Char(L1)^Char(Ln1)^Char(Ln2)^5;
Key1=((Char(Key1)<<4) & 240 ) | ((Char(Key1)>>4) & 15 ); // nibble swap
switch (Key1){
case 0: Key="/%DCN000%/";
break;
case 5: Key="/%DCN005%/";
break;
case 36: Key="/%DCN036%/";
break;
case 96: Key="/%DCN096%/";
break;
case 124: Key="/%DCN124%/";
break;
case 126: Key="/%DCN126%/";
break;
default: Key=Char(Key1);
}
// keyi
for (int i=2;i<LockLen;i++){
L1=Lock[i],Ln1=Lock[i-1];
Keyi=Char(L1)^Char(Ln1);
Keyi=((Char(Keyi)<<4) & 240 ) | ((Char(Keyi)>>4) & 15 ); // nibble swap
switch (Keyi){
case 0: Key=Key+"/%DCN000%/";
break;
case 5: Key=Key+"/%DCN005%/";
break;
case 36: Key=Key+"/%DCN036%/";
break;
case 96: Key=Key+"/%DCN096%/";
break;
case 124: Key=Key+"/%DCN124%/";
break;
case 126: Key=Key+"/%DCN126%/";
break;
default: Key=Key+Char(Keyi);
}
}
/**
* Contributed by dCoy
*/
public static String generateKey(String lockString){
int i = 0;
byte[] lock = null;
byte[] key = null;
lockString = lockString.substring(0,lockString.indexOf(' '));
lockString.trim();
lock = lockString.getBytes();
key = new byte[lock.length];
for(i=1;i<lock.length;i++){
key[i] = (byte)((lock[i] ^ lock[i-1]) & 0xFF);
}
key[0] = (byte)((((lock[0] ^ lock[lock.length-1]) ^ lock[lock.length-2]) ^ 5) & 0xFF);
for(i=0;i<key.length;i++){
key[i] = (byte)((((key[i]<<4) & 0xF0) | ((key[i]>>4) & 0x0F)) & 0xFF);
}
return(dcnEncode(new String(key)));
}
public static String dcnEncode(String string){
char[] replacements = null;
int i = 0;
int index = 0;
replacements = new char[]{0,5,36,96,124,126};
for(i=0;i<replacements.length;i++){
while((index = string.indexOf(replacements[i])) >=0 ){
string = string.substring(0,index)
+ "/%DCN"+leadz(replacements[i])+"%/"
+ string.substring(index+1,string.length());
}
}
return(string);
}
private static String leadz(int nr){
if(nr < 100 && nr > 10){
return("0"+nr);
} else if(nr < 10){
return("00"+nr);
} else{
return(""+nr);
}
}
Please note that the Strings class used in these two examples are contained inside Microsoft.VisualBasic and does therefore require that you add Microsoft.VisualBasic as a reference and that you add using Microsoft.VisualBasic; to the top of the sourcefile. (This is atleast needed for vs 2005 I can't confirm if it is needed for older version of the compiler aswell)
[edit] Example One
// By RaptoR franz@digital-wave.de
// This function assumes you have stripped $Lock and remove everything from
// Pk= onwards
// This code also works with yhub's extended char locks!
// Modified by Andreas Brekken <andreas@abrekken.com> for performance,
// readability, less code
public static string Decode( string aLock )
{
char[] key = new char[aLock.Length];
for (int i = 1; i < aLock.Length; i++)
{
key[i] = Strings.Chr(Strings.Asc(aLock[i])
^ Strings.Asc(aLock[i - 1]));
}
key[0] = Strings.Chr(Strings.Asc(aLock[0])
^ Strings.Asc(aLock[aLock.Length - 1])
^ Strings.Asc(aLock[aLock.Length - 2])
^ 5);
for (int i = 0; i < aLock.Length; i++)
{
key[i] = Strings.Chr(
((Strings.Asc(key[i]) << 4) & 240)
| ((Strings.Asc(key[i]) >> 4) & 15));
}
string keyString = "";
for (int i = 0; i < key.Length; i++)
{
int j = Strings.Asc(key[i]);
if (j != (int)key[i])
{
key[i] = (char)j;
}
keyString += key[i];
}
return escapeChars(keyString);
}
private static string escapeChars( string key )
{
System.Text.StringBuilder builder =
new System.Text.StringBuilder(key.Length);
for (int index=0; index<key.Length; index++)
{
int code = (int)key[index];
if (code == 0 || code == 5 || code == 36 || code == 96
|| code == 124 || code == 126)
builder.AppendFormat("/%DCN{0:000}%/", code);
else
builder.Append(key[index]);
}
return builder.ToString();
}
[edit] Example Two
public string LockToKey(string Lck)
{
/*
* This LockToKey is a direct translation of bluebear's VB.NET LockToKey
* into CSharp by kepp
* Originally written by bluebear - http://www.thewildplace.dk/
* Csharp translation was broken and fixed by bluebear
* If this don't work for you it's because you use the wrong encoding on
* inbound/outbound data (System.Text.Encoding.Default)
*/
/*
* Edited by Carraya (I hate bugs)
*/
Lck = Lck.Replace("$Lock ","");
string sKey;
string sTmp;
byte iLen;
int iChar;
int iPos = Lck.IndexOf(" Pk=",1);
if (Convert.ToBoolean(iPos))
Lck = Lck.Substring(0,iPos);
iChar = (Strings.Asc(Lck) ^ Strings.Asc(Lck.Substring(Lck.Length - 1)) ^ Strings.Asc(Lck.Substring(Lck.Length - 2, 1)) ^ 5);
iChar = (iChar / 16) ^ (iChar * 16);
while(iChar > 255)
{
iChar = iChar - 256;
}
if ((iChar == 0) || (iChar == 5) || (iChar == 36) || (iChar == 96) || (iChar == 124) || (iChar == 126))
{
sTmp = "00" + iChar.ToString();
iLen = Convert.ToByte(sTmp.Length);
if (iLen > 3)
iLen -= 3;
else
iLen = 0;
sKey = "/%DCN" + sTmp.Substring(iLen) + "%/";
}
else
{
sKey = Strings.Chr(iChar).ToString();
}
for(iPos = 1; iPos < Lck.Length; iPos++)
{
iChar = Strings.Asc(Lck.Substring(iPos, 1)) ^ Strings.Asc(Lck.Substring(iPos - 1, 1));
iChar = (iChar / 16) ^ (iChar * 16);
while(iChar > 255)
{
iChar = iChar - 256;
}
if ((iChar == 0) || (iChar == 5) || (iChar == 36) || (iChar == 96) || (iChar == 124) || (iChar == 126))
{
sTmp = "00" + iChar.ToString();
iLen = Convert.ToByte(sTmp.Length);
if (iLen > 3)
iLen -= 3;
else
iLen = 0;
sKey += "/%DCN" + sTmp.Substring(iLen) + "%/";
}
else
{
sKey += Strings.Chr(iChar).ToString();
}
}
return sKey;
}
[edit] Example Three
// In order to work properly, your client should be properly configured for the
// 1252-Windows encoding. Alternatively, you may have to change to byte[] for it
// to pass properly over a socket. Written by Emil Müller.
public string LockToKey(string lck)
{
string Key = "";
for (int i = 0, j; lck.Length > i; i++)
{
if (i == 0) j = lck[0] ^ 5;
else j = lck[i] ^ lck[i - 1];
for (j += ((j % 17) * 15); j > 255; j -= 255) ;
switch (j)
{
case 0:
case 5:
case 36:
case 96:
case 124:
case 126:
Key += "/%DCN" + ((string)("00" + j.ToString())).Substring(j.ToString().Length - 1) + "%/";
break;
default:
Key += (char)j;
break;
}
}
return (char)(Key[0] ^ Key[Key.Length - 1]) + Key.Substring(1);
}
[edit] Example Four
/*
* This LockToKey does NOT use Microsoft.VisualBasic as a reference
* also strips $Lock and Pk=
* Written by Gargol (gargol@gbot.nu)
*/
public string L2K(string lck)
{
lck = lck.Replace("$Lock ", "");
int iPos = lck.IndexOf(" Pk=", 1);
if (iPos > 0) lck = lck.Substring(0, iPos);
int[] arrChar = new int[lck.Length + 1];
int[] arrRet = new int[lck.Length + 1];
arrChar[1] = lck[0];
for (int i = 2; i < lck.Length + 1; i++)
{
arrChar[i] = lck[i - 1];
arrRet[i] = arrChar[i] ^ arrChar[i - 1];
}
arrRet[1] = arrChar[1] ^ arrChar[lck.Length] ^ arrChar[lck.Length - 1] ^ 5;
string sKey = "";
for (int n = 1; n < lck.Length + 1; n++)
{
arrRet[n] = ((arrRet[n] * 16) & 240) | ((arrRet[n] / 16) & 15);
int j = arrRet[n];
switch (j)
{
case 0:
case 5:
case 36:
case 96:
case 124:
case 126:
sKey += "/%DCN"
+ ((string)("00" + j.ToString())).Substring(j.ToString().Length - 1)
+ "%/";
break;
default:
sKey += Chr(Convert.ToByte((char)j));
break;
}
}
return sKey;
}
public static char Chr(byte src)
{
return (Encoding.Default.GetChars(new byte[] { src })[0]);
}
[edit] Visual FoxPro 6
* Coded By ZOseZEro @ Latvia
func LockToKey
para pcLock
priv Lock2Key, TLock2Key, TChar, i
Lock2Key=""
If Len(pcLock)<3
retu(Left("BROKENCLIENT", Len(pcLock)))
EndIf
TLock2Key = Chr(lXor(lXor(lXor(Asc(Left(pcLock, 1)), ;
Asc(Right(pcLock, 1))), ;
Asc(substr(pcLock, Len(pcLock) - 1, 1))), ;
5))
For i = 2 To Len(pcLock)
TLock2Key = TLock2Key + Chr(lXor(Asc(substr(pcLock, i, 1)), ;
Asc(substr(pcLock, i - 1, 1))))
Next
For i = 1 To Len(TLock2Key)
TChar = Asc(substr(TLock2Key, i, 1))
TChar = TChar * 16 + int(TChar / 16) &&Swap bits 11110000 -> 00001111
TChar = mod(TChar, 256)
If TChar = 0 Or TChar = 5 Or TChar = 36 Or TChar = 96 Or TChar = 124 Or TChar = 126
Lock2Key = Lock2Key + "/%DCN" + Right("000" + alltrim(str(TChar)),3) + "%/"
Else
Lock2Key = Lock2Key + Chr(TChar)
EndIf
Next
retu(Lock2Key)
End func
func lXor
para n1,n2
priv x,ret
ret=0
for x=1 to 8
if mod(n1,2) + mod(n2,2)=1
ret=ret+2^(x-1)
endif
n1=int(n1/2)
n2=int(n2/2)
next
retu (ret)
end func
[edit] mIRC 5.91+
; Coded by Mardeg
alias lock2key {
var %gl = $1, %gf = $base($xor($xor($xor($asc($left(%gl,1)),$asc($right(%gl,1))),$asc($left($right(%gl,2),1))),5),10,2,8)
var %gt = $+(/,%,DCN005,%,/,./,%,DCN000,%,/,./,%,DCN036,%,/,./,%,DCN096,%,/,./,%,DCN124,%,/,./,%,DCN126,%,/)
var %gb = 2, %ge = $len(%gl), %gn = 5.0.36.96.124.126, %gk
%gf = $base($+($right(%gf,4),$left(%gf,4)),2,10)
%gf = $iif($findtok(%gn,%gf,1,46),$gettok(%gt,$ifmatch,46),$chr(%gf))
while (%gb <= %ge) {
%gk = $base($xor($asc($mid(%gl,%gb,1)),$asc($mid(%gl,$calc(%gb - 1),1))),10,2,8)
%gk = $base($+($right(%gk,4),$left(%gk,4)),2,10)
%gf = $+(%gf,$iif($findtok(%gn,%gk,1,46),$gettok(%gt,$ifmatch,46),$chr(%gk)))
inc %gb
}
return %gf
}
[edit] Lua 5.0+
-- Coded by Mardeg
function bODD(x)
return x ~= math.floor(x / 2) * 2
end
function bitwise(x, y, bw)
local c, p = 0, 1
while x > 0 or y > 0 do
if bw == "xor" then
if (bODD(x) and not bODD(y)) or (bODD(y) and not bODD(x)) then
c = c + p
end
elseif bw == "and" then
if bODD(x) and bODD(y) then
c = c + p
end
elseif bw == "or" then
if bODD(x) or bODD(y) then
c = c + p
end
end
x = math.floor(x / 2)
y = math.floor(y / 2)
p = p * 2
end
return c
end
function nibbleswap(bits)
return bitwise(bitwise(bits*(2^4),240,"and"),bitwise(math.floor(bits/(2^4)),15,"and"),"or")
end
function lock2key(lock)
local key = {}
table.insert(key,bitwise(bitwise(bitwise(string.byte(lock,1),string.byte(lock,-1),"xor"),string.byte(lock,-2),"xor"),5,"xor"))
for i=2,string.len(lock),1 do
table.insert(key,bitwise(string.byte(lock,i),string.byte(lock,i - 1),"xor"))
end
local g = {["5"]=1,["0"]=1,["36"]=1,["96"]=1,["124"]=1,["126"]=1}
for i=1,table.getn(key),1 do
local b = nibbleswap(rawget(key,i))
rawset(key,i,(g[tostring(b)] and string.format("/%%DCN%03d%%/",b) or string.char(b)))
end
return table.concat(key)
end
[edit] JavaScript 1.5
// Coded by Mardeg
function nibbleswap(bits) {
return ((bits << 4) & 240) | ((bits >>> 4) & 15);
}
function chr(b) {
return (("..0.5.36.96.124.126.").indexOf("."+b+".")>0)?"/%DCN"+(0).toPrecision(4-b.toString().length).substr(2)+b+"%/":String.fromCharCode(b);
}
function lock2key(lock) {
var key = chr(nibbleswap(lock.charCodeAt(0) ^ lock.charCodeAt(-1) ^ lock.charCodeAt(-2) ^ 5));
for (var i=1; i<lock.length; i++) {
key += chr(nibbleswap(lock.charCodeAt(i) ^ lock.charCodeAt(i - 1)));
}
return key;
}
// Coded by Mardeg, tidied up by Andy Newman
static lock2key(lock)
{
f := [func (c)
{
g := [struct
(0) = "/%DCN000%/",
(5) = "/%DCN005%/",
(36) = "/%DCN036%/",
(96) = "/%DCN096%/",
(124) = "/%DCN124%/",
(126) = "/%DCN126%/"
];
c = ((c << 4) & 0xf0) | ((c >> 4) & 15);
b := g[c];
return b == NULL ? tochar(c) : b;
}];
len := nels(lock);
if (len < 3) return "BROKENCLIENT";
key := array(f(toint(lock[0]) ^ toint(lock[len-1]) ^ toint(lock[len-2]) ^ 5));
for (i := 1; i < len; ++i)
{
push(key, f(toint(lock[i]) ^ toint(lock[i - 1])));
}
return implode(key);
}
[edit] Erlang
%% Translated from Python code by Ivan Dubrov
lock_to_key(Lock) ->
[First | Tail] = Lock,
{Key, _} = lists:mapfoldl(fun(Cur, Prev) -> {Cur bxor Prev, Cur} end, First, Tail),
[Lock1, Lock2] = lists:nthtail(length(Lock) - 2, Lock), [Lock0 | _] = Lock,
Key2 = [Lock0 bxor Lock1 bxor Lock2 bxor 5 | Key],
Key3 = lists:map(fun(Val) -> <<Hi:4, Low:4>> = <<Val>>, <<Res>> = <<Low:4, Hi:4>>, Res end, Key2),
Key4 = lists:foldl(fun(Val, AccIn) ->
AccIn ++
if
Val == 0 -> "/%DCN000%/";
Val == 5 -> "/%DCN005%/";
Val == 36 -> "/%DCN036%/";
Val == 96 -> "/%DCN096%/";
Val == 124 -> "/%DCN124%/";
Val == 126 -> "/%DCN126%/";
true -> [Val]
end
end, [], Key3),
Key4.
# By C Erler with suggestions by Robert Klemme.
# Took the way of combining neighboring characters from the Python code.
def key(lock)
# We need a String of at least two bytes.
raise TypeError, "cannot convert #{lock.class} into String", caller unless lock.respond_to? :to_str
lock = lock.to_str
return '' unless lock.length >= 2
# Transform the input bytes.
result = Array.new(lock.length) { |i| lock[i - 1] ^ lock[i] }
result[0] ^= lock[-2] ^ 5
result.map! do |value|
# Rotate each byte by four bits.
value = ((value << 4) | (value >> 4)) & 0b11111111
# Put the output in the correct format.
case value
when 0, 5, 36, 96, 124, 126
'/%%DCN%03d%%/' % value
else
value.chr
end
end
# Combine the parts into the resultant string.
result.join
end